;;;
;;; solution3.lisp: Solution for selected exercises in LISP Tutorial 3
;;; Philip W. L. Fong
;;; SFU CMPT 310 (2001-1)
;;;

;;
;; Binary Trees
;;

(defun bin-tree-size (B)
  "Return the number of members in binary tree B."
  (if (bin-tree-leaf-p B)
      1
    (+ 1 
       (bin-tree-size (bin-tree-node-left B)) 
       (bin-tree-size (bin-tree-node-right B)))))

(defun bin-tree-postorder (B)
  "Create a list containing elements of B in postorder."
  (if (bin-tree-leaf-p B)
      (list (bin-tree-leaf-element B))
    (let
	((elmt  (bin-tree-node-element B))
	 (left  (bin-tree-node-left    B))
	 (right (bin-tree-node-right   B)))
      (append (bin-tree-postorder left)
	      (append (bin-tree-postorder right)
		      (cons elmt nil))))))

(defun fast-bin-tree-postorder (B)
  "A tail-recursive version of bin-tree-postorder."
  (postorder-aux B nil))

(defun postorder-aux (B A)
  "Append A to the end of the list containing elements of B in postorder."
  (if (bin-tree-leaf-p B)
      (cons (bin-tree-leaf-element B) A)
    (let
	((elmt  (bin-tree-node-element B))
	 (left  (bin-tree-node-left    B))
	 (right (bin-tree-node-right   B)))
      (postorder-aux left
		     (postorder-aux right 
				    (cons elmt A))))))

(defun bin-tree-inorder (B)
  "Create a list containing elements of B in inorder."
  (if (bin-tree-leaf-p B)
      (list (bin-tree-leaf-element B))
    (let
	((elmt  (bin-tree-node-element B))
	 (left  (bin-tree-node-left    B))
	 (right (bin-tree-node-right   B)))
      (append (bin-tree-inorder left) 
	      (cons elmt
		    (bin-tree-inorder right))))))

(defun fast-bin-tree-inorder (B)
  "A tail-recursive version of bin-tree-inorder."
  (inorder-aux B nil))

(defun inorder-aux (B A)
  "Append A to the end of the list containing elements of B in inorder."
  (if (bin-tree-leaf-p B)
      (cons (bin-tree-leaf-element B) A)
    (let
	((elmt  (bin-tree-node-element B))
	 (left  (bin-tree-node-left    B))
	 (right (bin-tree-node-right   B)))
      (inorder-aux left
		   (cons elmt
			 (inorder-aux right A))))))

;;
;; Sorted List
;;

(defun make-empty-sorted-list ()
  "Create empty sorted list."
  nil)

(defun sorted-list-empty-p (L)
  "Test if a sorted list L is empty."
  (null L))

(defun sorted-list-member-p (L E)
  "Test if element E is a member of a sorted list L."
  (if (null L)
      nil
    (if (> E (first L))
	(sorted-list-member-p (rest L) E)
      (= E (first L)))))

(defun sorted-list-insert (L E)
  "Insert element E into a sorted list L to produce a new sorted list."
  (if (null L)
      (list E)
    (if (> E (first L))
	(cons (first L) (sorted-list-insert (rest L) E))
      (if (= E (first L))
	  L
	(cons E L)))))

(defun sorted-list-remove (L E)
  "Remove element E from sorted list L to produce a new sorted list."
  (if (null L)
      nil
    (if (> E (first L))
	(cons (first L) (sorted-list-remove (rest L) E))
      (if (= E (first L))
	  (rest L)
	L))))

;;
;; Polynomials
;;
;; Newly introduced materials are tagged by ";-- new"
;;

;;
;; Constructors for polynomials
;;

(defun make-constant (num)
  num)

(defun make-variable (sym)
  sym)

;-- new
(defun make-negation (poly)
  (list '- poly))

(defun make-sum (poly1 poly2)
  (list '+ poly1 poly2))

;-- new
(defun make-difference (poly1 poly2)
  (list '- poly1 poly2))

(defun make-product (poly1 poly2)
  (list '* poly1 poly2))

(defun make-power (poly num)
  (list '** poly num))

;;
;; Recognizers for polynomials
;;

(defun constant-p (poly)
  (numberp poly))

(defun variable-p (poly)
  (symbolp poly))

;-- new
(defun negation-p (poly)
  (and (listp poly) (eq (first poly) '-) (null (rest (rest poly)))))

(defun sum-p (poly)
  (and (listp poly) (eq (first poly) '+)))

;-- new
(defun difference-p (poly)
  (and (listp poly) (eq (first poly) '-) (not (null (rest (rest poly))))))

(defun product-p (poly)
  (and (listp poly) (eq (first poly) '*)))

(defun power-p (poly)
  (and (listp poly) (eq (first poly) '**)))

;;
;; Selectors for polynomials
;;

(defun constant-numeric (const)
  const)

(defun variable-symbol (var)
  var)

;-- new
(defun negation-arg (neg)
  (second neg))

(defun sum-arg1 (sum)
  (second sum))

(defun sum-arg2 (sum)
  (third sum))

;-- new
(defun difference-arg1 (diff)
  (second diff))

;-- new
(defun difference-arg2 (diff)
  (third diff))

(defun product-arg1 (prod)
  (second prod))

(defun product-arg2 (prod)
  (third prod))

(defun power-base (pow)
  (second pow))

(defun power-exponent (pow)
  (third pow))

;;
;; Unevaluated derivative
;;

(defun make-derivative (poly x)
    (list 'd poly x))

(defun derivative-p (poly)
  (and (listp poly) (eq (first poly) 'd)))

;;
;; Differentiation function
;;

(defun d (poly x)
  (cond
   ((constant-p poly) 0)
   ((variable-p poly) 
    (if (equal poly x) 
	1 
      (make-derivative poly x)))
   ;-- new
   ((negation-p poly)
    (make-negation (d (negation-arg poly) x)))
   ((sum-p poly) 
    (make-sum (d (sum-arg1 poly) x) 
	      (d (sum-arg2 poly) x)))
   ;-- new
   ((difference-p poly)
    (make-difference (d (difference-arg1 poly) x)
		     (d (difference-arg2 poly) x)))
   ((product-p poly) 
    (make-sum (make-product (product-arg1 poly) 
			    (d (product-arg2 poly) x))
	      (make-product (product-arg2 poly) 
			    (d (product-arg1 poly) x))))
   ((power-p poly)
    (make-product (make-product (power-exponent poly)
				(make-power (power-base poly) 
					    (1- (power-exponent poly))))
		  (d (power-base poly) x)))))

;;
;; Simplification function
;;

(defun simplify (poly)
  "Simplify polynomial POLY."
  (cond
   ((constant-p poly) poly)
   ((variable-p poly) poly)
   ;-- new
   ((negation-p poly) 
    (let ((arg (simplify (negation-arg poly))))
      (make-simplified-negation arg)))
   ((sum-p poly)
    (let ((arg1 (simplify (sum-arg1 poly)))
	  (arg2 (simplify (sum-arg2 poly))))
      (make-simplified-sum arg1 arg2)))
   ((product-p poly)
    (let ((arg1 (simplify (product-arg1 poly)))
	  (arg2 (simplify (product-arg2 poly))))
      (make-simplified-product arg1 arg2)))
   ;-- new
   ((difference-p poly)
    (let ((arg1 (simplify (difference-arg1 poly)))
	  (arg2 (simplify (difference-arg2 poly))))
      (make-simplified-difference arg1 arg2)))
   ((power-p poly)
    (let ((base (simplify (power-base poly)))
	  (exponent (simplify (power-exponent poly))))
      (make-simplified-power base exponent)))
   ((derivative-p poly) poly)))

;-- new
(defun make-simplified-negation (arg)
  "Given simplified polynomial ARG, construct a simplified negation of ARG."
  (cond
   ((and (constant-p arg) (zerop arg)) arg)
   ((negation-p arg)                   (negation-arg arg))
   (t                                  (make-negation arg))))

(defun make-simplified-sum (arg1 arg2)
  "Given simplified polynomials ARG1 and ARG2, construct a simplified sum of ARG1 and ARG2."
  (cond
   ((and (constant-p arg1) (zerop arg1)) arg2)
   ((and (constant-p arg2) (zerop arg2)) arg1)
   ;-- new
   ((negation-p arg1)                   (make-simplified-difference 
					 arg2 (negation-arg arg1)))
   ;-- new
   ((negation-p arg2)                   (make-simplified-difference 
					 arg1 (negation-arg arg2)))
   (t                                   (make-sum arg1 arg2))))

;-- new
(defun make-simplified-difference (arg1 arg2)
  "Given simplified polynomials ARG1 and ARG2, construct a simplified difference of ARG1 and ARG2."
  (cond
   ((and (constant-p arg2) (zerop arg2)) arg1)
   ((and (constant-p arg1) (zerop arg1)) (make-simplified-negation arg2))
   ((negation-p arg2)                    (make-simplified-sum 
					  arg1 (negation-arg arg2)))
   (t                                    (make-difference arg1 arg2))))

(defun make-simplified-product (arg1 arg2)
  "Given simplified polynomials ARG1 and ARG2, construct a simplified product of ARG1 and ARG2."
  (cond
   ((and (constant-p arg1) (zerop arg1)) (make-constant 0))
   ((and (constant-p arg2) (zerop arg2)) (make-constant 0))
   ((and (constant-p arg1) (= arg1 1))   arg2)
   ((and (constant-p arg2) (= arg2 1))   arg1)
   ;-- new
   ((and (constant-p arg1) (= arg1 -1))  (make-simplified-negation arg2))
   ;-- new
   ((and (constant-p arg2) (= arg2 -1))  (make-simplified-negation arg1))
   (t                                    (make-product arg1 arg2))))

(defun make-simplified-power (base exponent)
  "Given simplified polynomials BASE and EXPONENT, construct a simplified power with base BASE and exponent EXPONENT."
  (cond
   ((and (constant-p exponent) (= exponent 1))   base)
   ((and (constant-p exponent) (zerop exponent)) (make-constant 1))
   (t                                            (make-power base exponent))))
